29|Session与Cookie:账户体系的安全设计原理

你好,我是王昊天。
我有次在访问某个页面时,为了下载一些东西,按照页面要求进行了复杂的登录操作。之后我不小心关闭了当前页面,然后再一次点开这个页面,麻木的准备再来一遍复杂的登录操作时,我神奇地发现,面前的 Web 应用竟然是登录成功的状态,你知道这是怎么一回事吗?
事实上,这个现象是由 Web 账户体系的安全设计所导致的。在这一讲中,我们将会对它进行学习,这样你就能清楚地知道问题的答案啦。下面我们就正式开始今天的学习。
现在几乎每个大型 Web 应用都会存在账户体系,当我们需要获取 Web 应用中的某些服务时,Web 应用会首先对我们的身份进行认证。所以接下来,我们会从身份认证的相关基础知识入手。

身份认证

身份认证的方式有多种,我们可以用最典型的账号密码进行认证,除此之外,我们还可以用 cookie(session)、Token、数字证书以及手机验证码来验证。这里你可能对于 cookie 以及 Token 会比较陌生,不过不用担心,我们会在后面对它们进行详细的讲解。
在这些认证过程中,可以分为两种类型,即登录过程的认证以及保持登录的认证
为了让你更好地理解它们二者之间的区别,我们一起来看一个示例。
这是谜团(mituan.zone)的登录页面,我们需要输入正确的用户名、密码以及验证码才能通过身份认证,很明显这是登录过程的认证。
当我们登录成功后,我们会发现浏览器中多了一些 cookie 信息。
这些 cookie 信息有一定的有效期。在有效期内,cookie 信息会一直存在,它使得我们下次访问这个页面时,无需再次输入账号密码进行登录,而是可以直接用 cookie 信息来实现身份认证操作,这就是保持登录的认证。
你现在知道导入中神奇现象发生的原因了吗?其实在导入部分中的自动登录,就是通过保持登录的认证来实现的。而这种认证方式,主要是通过会话管理来实现的,接下来让我们简单了解下会话管理的作用。

会话管理

在学习会话管理之前,我们首先需要巩固下 HTTP 协议的知识。
HTTP 协议是无状态无连接的协议,服务端对于客户端每次发送的请求都认为它是一个新的请求,上一次会话和下一次会话是没有联系的。因为它无法保存登录状态,所以从协议本身来说,它不适合用来做会话管理。
因此,我们会使用一个上层应用去实现我们的会话管理功能。这个应用可以在切换页面时保持登录状态,并且对用户是透明的,这样就使得我们能在短时间内再次访问一个登录过的页面,就会保持登录状态。
经过上述内容的学习,你已经知道了会话管理具有什么作用。接下来,让我们具体学习下会话管理的两种典型方式,即基于 session 的认证以及基于 Token 的认证。

基于 session 的认证

Web 应用可以基于 session 的认证来实现保持登录,它的具体实现方式如下图所示:
用户在首次访问 Web 应用时,会将自己的账号密码通过 POST 方式进行上传,然后 Web 应用服务器会对账号密码进行检查。如果检查通过就会给用户配置一个 sessionid,并将它存储在服务器内存中,之后再把这个 sessionid 发送给用户。
注意这里 sessionid 的位置可能在 URL、隐藏域以及 cookie 中。由于 cookie 信息较为隐蔽些,所以将 sessionid 放在 cookie 中相对来说更为安全,因此这一实现方式也最普遍。
用户在收到 Web 应用服务器的回应之后,再次对 Web 应用发起请求的 cookie 中就会自动包含 sessionid 信息。Web 应用服务器会对其中的 sessionid 信息进行检查,以获取用户的登录信息,如果信息正确,就让用户处于登录成功的状态,否则需要重新进行登录过程的认证。
值得一提的是,为了安全考虑,Web 应用通常会给 sessionid 设置一个过期时间,使得 sessionid 仅在某个时间段内有效,这样就可以有效地抵御攻击者盗用 sessionid 绕过身份认证的行为。
到这里,我们已经学习了 Web 应用是如何利用 session 进行身份认证的。而这里还有一个很重要的知识点我们有必要深入了解一下,那就是在 session 进行身份认证中存在的典型攻击方式——会话固定攻击
在之前的学习中,我们知道了 sessionid 可以存在于 URL 中。在这种情况下,如果登录前后 sessionid 不变化,那么攻击者就可以发起会话固定攻击。
这里我已经画出了会话固定攻击的示意图,让我们一起看看吧。
攻击者首先访问一个需要登录的网站,获取到 Web 应用返回的 sessionid 信息。由于攻击者没有账户密码,所以只能通过发送一个诱骗信息给受害者,使得受害者用这个 sessionid 实现登录操作。这样攻击者的 sessionid 就通过了验证,使得攻击者再次用这个 sessionid 信息访问被攻击网站时,可以直接通过保持登录的认证。
这就是将 sessionid 信息放在 URL 中的安全隐患。

基于 Token 的认证

除了基于 session 的认证之外,Web 应用还可以利用 Token 来实现会话管理。
基于 Token 的认证方式,如下图所示,让我们从图中观察它是如何实现的吧。
用户首先需要通过 POST 方式上传账号密码信息,进行登录过程的认证,Web 应用服务器接收到之后,会检查账号密码信息是否正确,如果正确就会生成一个包含密码信息的 Token 值,这里以 JWT(JSON Web Token)为例。
之后服务器会将这个 Token 信息发送我们的浏览器,接着浏览器会将这个 Token 信息保存在 Header 中,使得以后每次请求的 Header 中都会包含这个 Token 信息。服务器在接收到 Token 信息后,会从中提取出用户的账户信息,并对此进行检测,然后将响应发送给我们的浏览器。
这就是基于 Token 的认证方式,下面让我们以 JWT 为例进行学习,深入地了解 Token 的具体形式。
上方方框中的内容是一个完整的 JWT 信息,它可以根据. 分割成三个部分,我们将它不同的部分用不同的颜色进行显示。接下来,让我们逐一分析 JWT 各个部分的内容。
第一个部分经过 base64 解码就变为了蓝色方框中的内容,其中 alg 的内容设置的是 signature 中签名使用的算法,而 typ 的内容则定义了这个 Token 的类型。
第二部分解码为绿色方框中的内容,它包含了用户相关的信息,Web 应用可以根据这些信息来确定用户的身份。
最后一部分解码为橙色方框中的内容,它包含了对 Token 信息的完整性验证签名。其中需要用到仅有服务器知道的 secret 信息,这也是导致攻击者无法伪造 Token 信息的关键。
以上就是 JWT 的组成结构。其中 Header 以及 payload 用到的都是些通用数据,攻击者很容易就可以伪造出来。唯一有难度的就是对 secret 签名部分的伪造,事实上,攻击者可以通过密钥爆破的方式,尝试进行 Signature 信息的伪造。一旦伪造成功,攻击者就可以以任意身份登录这个 Web 应用,这对 Web 应用来说是极大的威胁。所以 Token 信息的设计者,需要有意识地提高 secret 的复杂度。
到这里,你已经学习了会话管理的两种典型方式。接下来,让我们拓宽视野,简单了解下单点登录的知识。

单点登录

如今的 Web 应用越来越多,同一个公司可能就会研发出多个 Web 应用,如果每个应用都需要分开登录注册,那既会使得用户感到不方便,也会增加开发成本。为了解决这个问题,大家通常会采取单点登录方案。
单点登录就是用户只需要登录一次就可以访问所有相互信任的应用系统。它把认证的流程统一起来,使得认证的风险集中化。
这样,我们只需要在那统一的登录流程中做好安全认证措施,就可以实现对多个应用的身份认证。单点登录既能降低开发成本,也可以提高登录的安全性。

总结

这节课我们学习了账户体系的安全认证设计。
首先,我们学习了身份认证的方式,了解到除了我们熟悉的登录过程认证之外,还有保持登录认证这一种方式。
接着,我们深入学习了保持登录认证的方式,知道了它是由会话管理方法实现的。然后我们对基于 session 的会话管理以及基于 Token 的会话管理进行了全面的学习,我们不仅知道了它们保持登录认证的实现方式,还知道它们存在的安全隐患。
最后,我们了解了一个面对多个应用需要登录验证时的解决方案,即单点登录。使用单点登录既可以统一管理所有的登录认证,还可以降低多个 Web 应用的开发成本。

思考题

你知道在基于 session 的保持登录认证中,为什么将 session 信息放置在 cookie 中会更加安全吗?
欢迎在评论区留下你的思考。如果觉得今天的内容对你有所帮助的话,也欢迎你把课程分享给其他同事或朋友,我们共同学习进步!
上一篇
28|安全配置错误:安全问题不只是代码安全
下一篇
30|HTTP Header安全标志:协议级别的安全支持